package org.orbroker

import java.sql._
import org.orbroker.exception._

/**
 * The public interface for querying and 
 * calling procedures in either
 * read-only mode, or transactional.
 */
trait QuerySession extends Queryable {

  private def call[OP, T](
    token: Token[T], 
    cstm: CallStatement,
    parms: Seq[(String, Any)],
    receivers: Seq[T ⇒ Boolean],
    outParmHandler: Option[(OutParms) ⇒ OP]): OP = {
    val startTime = System.nanoTime
    try {
      val (sql, values, rowsUpdated, outParm) =
        cstm.call(token, this, toMap(parms), None, receivers, outParmHandler)
      callback.onSQL(token.id, sql, values, diffTimeInMicros(startTime))
      if (readOnly && rowsUpdated > 0) {
        throw new StoredProcedureUpdateException(token.id, rowsUpdated)
      }
      outParm
    } catch {
      case e: SQLException ⇒ throw evaluate(e)
    }
  }

  protected def queryFromCall[T](token: Token[T], cs: CallStatement, parms: Seq[(String, Any)], receiver: T ⇒ Boolean) = {
    token.extractor match {
      case ph: OutParmExtractor[_] ⇒ {
        val op = call[T, T](token, cs, parms, Seq.empty, Some(ph))
        receiver.apply(op)
      }
      case _ ⇒ call(token, cs, parms, Seq(receiver), None)
    }
  }

  def callForParms[T](token: Token[_], parms: (String, Any)*)(ph: OutParms ⇒ T): T = {
    val cs = getCallStatement(token)
    call[T, Any](token.asInstanceOf[Token[Any]], cs, parms, Seq.empty, Some(ph))
  }
}
